再谈 Cookie 和 Session 安全性
0X00 前言
最近整理一些关于业务安全的东西,然后又遇到了这个问题,虽然自己每次提到这个问题第一反应都是一个是在服务器端保存另一个是在客户端保存,但我知道这并不是正确的答案,因为 session 也需要在客户端保存一个标识符 session_id,所以还是想再写一下这个问题,因为实际上一年前左右我已经写过一篇关于 cookie 和 session 的文章,有关 cookie 和session的一些探究,那这篇文章就叫做“再谈”吧。
0X01 Cookie 与 Session 的博弈
注意:
攻击者有两种方式获取 cookie ,一种是通过中间人攻击,一种是利用 XSS 这里我们就不讨论中间人攻击对两者的影响,因为这个可以使用 SSL 进行传输防止中间人攻击,设置 cookie 的时候只要加一个Secure 选项就可以保证只在 https 的情况下传输 cookie 了。
1.能否防止重放攻击
还是回到刚刚说的那个问题,如果说 session 是保存在服务器端的 cookie 是保存在客户端是两者的最大区别的话,我不反对,但是说这是他们安全性不同的原因的话,我认为是非常片面的
熟悉 XSS 的人都知道,在 cookie 没有设置 http-only 的情况下获取 cookie 的值是非常轻松的,这也是人们常说的 cookie 是不安全的原因之一,这固然不可否认。
但是你要知道 session 他也并不是完全的就放在服务器端而和客户端没有半毛钱关系,想想也不可能,要不服务器端怎么确定客户端是谁啊,所以他必须在客户端放一个标识符 session-id,我们可以理解为 服务器端 session 数据库的一个索引,客户端每次上交这个标识符给服务器端然后服务器端根据这个标识在数据库中进行检索,从而判定用户的身份。
所以,从这一点来看,cookie 和 session 没什么本质的区别,session_id 也是放在 cookie 中的,他们都不能防止被窃取从而进行重放攻击。
2.能否防止篡改攻击
session 设置了以后存储在服务器端,客户端看到的只是一个session_id,这就相当于起到了一个隐藏或者说加密的作用
比如说你设置
$SESSION['user']='admin';
这个返回的header就是set-cookie: sessionId:xxxxxxx(一堆加密了的字符)
但是你设置 cookie 他默认就不会加密,设置啥就显示啥
set_cooke('user', 'xiaoming');
它返回的header就是 set-cookie: user:xiaoming
那么这一点就可以作为两者安全性的一个重要指标,这就可以防止会话的伪造攻击,攻击者即使是获取了整个 cookie 的数据(这里特指里面的 session_id,至于cookie 中除了 session_id 以外的其他内容是一些临时的数据这里不做讨论)但是攻击者并不能理解 session_id 的内容,最多只能是利用这个session_id 进行伪造,但是不能篡改 session_id 进行越权(包括水平越权和垂直越权),而单纯使用 cookie 就不一样了 ,你看我上面这个 Cookie就是明文的,什么信息在里面一清二楚。如果攻击者拿到了这个 cookie 的话,那么就很容易联想到可以将 xiaoming 改成 admin 这类的,从而进行越权。
3.新的思考
那有人说了,既然这样我们代码里对这个 cookie 加密一下不就完了,不是起到了一样的效果?就像下面这个样子
set_cookie('user', some_encrypt('xiaoming', 'privatekey','timestamp'));
没错,这样确实起到了防止 cookie 伪造的作用,那这样看 session 还有什么用呢?为什么不是选择仅仅加密一下 cookie 作为防止攻击的手段,而要费尽心思的设计这个 session呢?session 的优势在这里又体现在什么地方呢?
原因大致有以下两点:
(1)开发的方便性和系统的安全性
在实际开发的时候,session的实现和应用已经很成熟。可以将其做分布式的储存和共享,它的加密算法也无需再去进行更改或者什么的,这样就不需要每个开发者自己思考用什么算法,这样会导致很不统一,并且也增加了开发人员的负担,同时这也避免了开发者自己去设计一些乱七八糟的算法,不合理的设计和使用反而会降低系统的安全性。
(2)数据包的大小
我们知道用户的信息还是比较多的,如果我们使用加密算法在本地进行加密传输的话,加密后数据会膨胀的很大,这样就会增加数据包的大小,这是很不好的。
综上,使用 session 在安全性和实用性上要比单纯使用 cookie 保存用户信息更优越
0X02 常见问题
我在网站寻找类似的文章的时候看到一些结论,有些是比较片面的,或者说只是其中的一种情况而已,下面来纠正一下。
问题一:cookie/session_id 在浏览器关闭后就失效了
首先这句话肯定是错的,因为这需要一个前提条件,条件就是:默认情况下,我们知道 cookie 在设置的时候是可以设置生存时间的,就像下面这个样子
setcookie('sessid', 'uniqid', time()+3600, '/', '', true, true);
我们可以将其设置为很长一段时间,那么这样的话,cookie 或者说 session_id 就会被浏览器保存在硬盘里长期有效。
补充:
session 的默认生存时间是 20 分钟,也就是说你不另外设置的话 session_id 20分钟后就失效了
问题二:cookie分为二种,以文件方式存在硬盘空间上的长期性的cookie和停留在浏览器所占内存中的临时性的cookie
这句话其实还是片面的,只能说从时间上划分可以这样将 cookie 分成两类,但是他后面这么说(我这里就直接截图了):
我是在是不懂这是在说啥,巨大的误导……他似乎把 cookie 和 session_id 完全分开了,其实cookie 中真正保存用户信息的就是 session_id
我们选择记住自己的登录状态其实就是设置比较长的 cookie 或者说 session_id 的生存时间,然后让其保存在客户端的硬盘上。
0X03 参考链接
http://www.cnblogs.com/demingblog/p/3878185.html
0X04 总结
这篇文章其实是我一直以来都想写的,但是由于时间关系以及自己的理解还不是特别的到位,不知道该怎么表达,今天因为又遇到了这个问题,我就又借此机会好好的思考了一下,也请教了我的一些专业的开发的学长,写下这篇文章,也是希望让更多的和我曾经一样对此困惑的人有一个参考,当然我依然不能保证我说的完全正确,我希望更多专业的人能帮助我发现这篇文章中的问题并联系我加以纠正,以免误导更多的人。